Imports Microsoft.VisualBasic
Imports System
Imports System.Collections.Generic
Imports System.Text
Imports System.Xml
Imports System.ComponentModel
Imports System.Globalization
Imports C4F.DevKit.WebServices.Datasets

Namespace C4F.DevKit.WebServices

	''' <summary>
	''' Exposes search functionality on Flickr.com
	''' </summary>
	<ToolboxItem(True)> _
	Public Class Flickr
		Inherits C4FWebServicesBase
		#Region "Private members"
		''' <summary>
		''' Base url
		''' </summary>
        Private baseUrl As String = My.Resources.FlickrBaseUrl
		''' <summary>
		''' Application key
		''' </summary>
		Private applicationKey_Renamed As String = String.Empty

		#End Region

		#Region "Accessors"
		''' <summary>
		''' Access Key required to use the Api
		''' </summary>
		<Category("Flickr"), Description("Access Key required to use the Api")> _
		Public Property ApplicationKey() As String
			Get
				If Me.applicationKey_Renamed Is Nothing Then
					Return String.Empty
				Else
					Return Me.applicationKey_Renamed.Trim()
				End If
			End Get
			Set(ByVal value As String)
				Me.applicationKey_Renamed = value.Trim()
			End Set
		End Property

		#End Region

		#Region "Constructors"
		''' <summary>
		''' Initializes a new object of Flickr class
		''' </summary>
		Public Sub New()
		End Sub
		''' <summary>
		''' Initializes a new object of Flickr class with an application key
		''' </summary>
		''' <param name="applicationKey">Application key</param>
        Public Sub New(ByVal applicationKey As String)
            Me.applicationKey_Renamed = ApplicationKey
        End Sub

		#End Region

		#Region "Public functions"

		''' <summary>
		''' Searches for a list of photos matching the criteria
		''' </summary>
		''' <param name="userId">Id of the user who's photo to search</param>
		''' <param name="tags">List of tags</param>
		''' <param name="tagMode">tag mode (Either 'any' for an OR combination of tags or 'all' for an AND combination)</param>
		''' <param name="text">A free text search</param>
		''' <param name="minUploadDate">Minimum upload date</param>
		''' <param name="maxUploadDate">Maximum upload date</param>
		''' <param name="minTakenDate">Minimum taken date</param>
		''' <param name="maxTakenDate">Maximum taken date</param>
		''' <param name="license">The license id for photos</param>
		''' <param name="sort">The order in which to sort returned photos</param>
		''' <param name="privacyFilter">Return photos only matching a certain privacy level</param>
		''' <param name="bbox">A list of 4 values defining the Bounding Box of the area that will be searched</param>
		''' <param name="accuracy">Recorded accuracy level of the location information</param>
		''' <param name="extras">A list of extra information to fetch for each returned record</param>
		''' <param name="numberOfPagesForResult">The page of results to return</param>
		''' <param name="resultsPerPage">Results per page</param>
		''' <returns>A list of photos matching the criteria</returns>
		''' <exception cref="InvalidOperationException">The Application Key is not set</exception>
		Public Function Search(ByVal userId As String, ByVal tags As String, ByVal tagMode As String, ByVal text As String, ByVal minUploadDate As String, ByVal maxUploadDate As String, ByVal minTakenDate As String, ByVal maxTakenDate As String, ByVal license As String, ByVal sort As String, ByVal privacyFilter As Nullable(Of Integer), ByVal bbox As String, ByVal accuracy As Nullable(Of Integer), ByVal extras As String, ByVal numberOfPagesForResult As Nullable(Of Integer), ByVal resultsPerPage As Nullable(Of Integer)) As List(Of PhotoData)
			If String.IsNullOrEmpty(Me.ApplicationKey) Then
                Throw New InvalidOperationException(My.Resources.ErrorApplicationKeyNotSet)
            End If

            Dim requestUrl As String = Me.GetQueryString(userId, tags, tagMode, text, minUploadDate, maxUploadDate, minTakenDate, maxTakenDate, license, sort, privacyFilter, bbox, accuracy, extras, numberOfPagesForResult, resultsPerPage)

            ' Get Flickr responses
            Dim responseData As String = MyBase.GetQueryResponse(requestUrl)

            ' Parse the data and return results
            Dim photoDataList As List(Of PhotoData) = ParseResponseXml(responseData)

            ' Fill up the photo url using flicke.photo.getinfo methos.
            FillPhotoUrl(photoDataList)

            Return photoDataList
        End Function

        ''' <summary>
        ''' Searches for a list of photos matching the criteria
        ''' </summary>
        ''' <param name="text">A free text search</param>
        ''' <returns>A list of photos matching the criteria</returns>
        ''' <exception cref="InvalidOperationException">The Application Key is not set</exception>
        ''' <exception cref="ArgumentException">The text is not specified</exception>
        Public Function Search(ByVal text As String) As List(Of PhotoData)
            If String.IsNullOrEmpty(Me.ApplicationKey) Then
                Throw New InvalidOperationException(My.Resources.ErrorApplicationKeyNotSet)
            End If
            If String.IsNullOrEmpty(text) Then
                Throw New ArgumentException(String.Format(CultureInfo.InvariantCulture, My.Resources.ErrorSpecifyArgument, "text"))
            End If

            Dim requestUrl As String = Me.GetQueryString(Nothing, Nothing, Nothing, text, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing)

            ' Get Flickr responses
            Dim responseData As String = MyBase.GetQueryResponse(requestUrl)

            ' Parse the data and return results
            Dim photoDataList As List(Of PhotoData) = ParseResponseXml(responseData)

            ' Fill up the photo url using flicke.photo.getinfo methos.
            FillPhotoUrl(photoDataList)

            Return photoDataList
        End Function

        ''' <summary>
        ''' Searches for a list of photos matching the criteria
        ''' </summary>
        ''' <param name="userId">Id of the user who's photo to search</param>
        ''' <param name="tags">List of tags</param>
        ''' <returns>A list of photos matching the criteria</returns>
        ''' <exception cref="InvalidOperationException">The Application Key is not set</exception>
        ''' <exception cref="ArgumentException">The userId is not specified (OR) the tags are not specified</exception>
        Public Function Search(ByVal userId As String, ByVal tags As String) As List(Of PhotoData)
            If String.IsNullOrEmpty(Me.ApplicationKey) Then
                Throw New InvalidOperationException(My.Resources.ErrorApplicationKeyNotSet)
            End If

            If String.IsNullOrEmpty(userId) Then
                Throw New ArgumentException(String.Format(CultureInfo.InvariantCulture, My.Resources.ErrorSpecifyArgument, "userId"))
            End If

            If String.IsNullOrEmpty(tags) Then
                Throw New ArgumentException(String.Format(CultureInfo.InvariantCulture, My.Resources.ErrorSpecifyArgument, "tag"))
            End If

            Dim requestUrl As String = Me.GetQueryString(userId, tags, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing)

            ' Get Flickr responses
            Dim responseData As String = MyBase.GetQueryResponse(requestUrl)

            ' Parse the data and return results
            Dim photoDataList As List(Of PhotoData) = ParseResponseXml(responseData)

            ' Fill up the photo url using flicke.photo.getinfo methos.
            FillPhotoUrl(photoDataList)

            Return photoDataList
        End Function

        ''' <summary>
        ''' Gets a list of photos of the specified user
        ''' </summary>
        ''' <param name="userId">Id of the user who's photo to search</param>
        ''' <returns>A list of photos matching the criteria</returns>
        ''' <exception cref="InvalidOperationException">The Application Key is not set</exception>
        ''' <exception cref="ArgumentException">The userId is not specified</exception>
        Public Function SearchByUserId(ByVal userId As String) As List(Of PhotoData)
            If String.IsNullOrEmpty(Me.ApplicationKey) Then
                Throw New InvalidOperationException(My.Resources.ErrorApplicationKeyNotSet)
            End If

            If String.IsNullOrEmpty(userId) Then
                Throw New ArgumentException(String.Format(CultureInfo.InvariantCulture, My.Resources.ErrorSpecifyArgument, "userId"))
            End If

            Dim requestUrl As String = Me.GetQueryString(userId, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing)

            ' Get Flickr responses
            Dim responseData As String = MyBase.GetQueryResponse(requestUrl)

            ' Parse the data and return results
            Dim photoDataList As List(Of PhotoData) = ParseResponseXml(responseData)

            ' Fill up the photo url using flicke.photo.getinfo methos.
            FillPhotoUrl(photoDataList)

            Return photoDataList
        End Function

		''' <summary>
		''' Searches for a list of photos matching the criteria and returns the result in a dataset
		''' </summary>
		''' <param name="userId">Id of the user who's photo to search</param>
		''' <param name="tags">List of tags</param>
		''' <param name="tagMode">tag mode (Either 'any' for an OR combination of tags or 'all' for an AND combination)</param>
		''' <param name="text">A free text search</param>
		''' <param name="minUploadDate">Minimum upload date</param>
		''' <param name="maxUploadDate">Maximum upload date</param>
		''' <param name="minTakenDate">Minimum taken date</param>
		''' <param name="maxTakenDate">Maximum taken date</param>
		''' <param name="license">The license id for photos</param>
		''' <param name="sort">The order in which to sort returned photos</param>
		''' <param name="privacyFilter">Return photos only matching a certain privacy level</param>
		''' <param name="bbox">A list of 4 values defining the Bounding Box of the area that will be searched</param>
		''' <param name="accuracy">Recorded accuracy level of the location information</param>
		''' <param name="extras">A list of extra information to fetch for each returned record</param>
		''' <param name="numberOfPagesForResult">The page of results to return</param>
		''' <param name="resultsPerPage">Results per page</param>
		''' <returns>Dataset containing list of photos matching the criteria</returns>
		''' <exception cref="InvalidOperationException">The Application Key is not set</exception>
		Public Function Search_DS(ByVal userId As String, ByVal tags As String, ByVal tagMode As String, ByVal text As String, ByVal minUploadDate As String, ByVal maxUploadDate As String, ByVal minTakenDate As String, ByVal maxTakenDate As String, ByVal license As String, ByVal sort As String, ByVal privacyFilter As Nullable(Of Integer), ByVal bbox As String, ByVal accuracy As Nullable(Of Integer), ByVal extras As String, ByVal numberOfPagesForResult As Nullable(Of Integer), ByVal resultsPerPage As Nullable(Of Integer)) As DS_Flickr
			Dim photos As List(Of PhotoData) = Search(userId, tags, tagMode, text, minUploadDate, maxUploadDate, minTakenDate, maxTakenDate, license, sort, privacyFilter, bbox, accuracy, extras, numberOfPagesForResult, resultsPerPage)
			Dim dataset As DS_Flickr = New DS_Flickr()
			For Each photo As PhotoData In photos
				Dim photoDetailsRow As DS_Flickr.PhotoDetailsRow = dataset.PhotoDetails.AddPhotoDetailsRow(photo.PhotoDetails.Owner, photo.PhotoDetails.Secret, photo.PhotoDetails.Server, photo.PhotoDetails.Farm, photo.PhotoDetails.IsPublic, photo.PhotoDetails.IsFriend, photo.PhotoDetails.IsFamily)

				dataset.PhotoData.AddPhotoDataRow(photo.Id, photo.Title, photo.Url, photoDetailsRow)
			Next photo
			Return dataset
		End Function

		''' <summary>
		''' Searches for a list of photos matching the criteria and returns the result in a dataset
		''' </summary>
		''' <param name="userId">Id of the user who's photo to search</param>
		''' <param name="tags">List of tags</param>
		''' <returns>Dataset containing list of photos matching the criteria</returns>
		''' <exception cref="InvalidOperationException">The Application Key is not set</exception>
		''' <exception cref="ArgumentException">The userId is not specified (OR) the tags are not specified</exception>
		Public Function Search_DS(ByVal userId As String, ByVal tags As String) As DS_Flickr
			Dim photos As List(Of PhotoData) = Search(userId, tags)
			Dim dataset As DS_Flickr = New DS_Flickr()
			For Each photo As PhotoData In photos
				Dim photoDetailsRow As DS_Flickr.PhotoDetailsRow = dataset.PhotoDetails.AddPhotoDetailsRow(photo.PhotoDetails.Owner, photo.PhotoDetails.Secret, photo.PhotoDetails.Server, photo.PhotoDetails.Farm, photo.PhotoDetails.IsPublic, photo.PhotoDetails.IsFriend, photo.PhotoDetails.IsFamily)

				dataset.PhotoData.AddPhotoDataRow(photo.Id, photo.Title, photo.Url, photoDetailsRow)
			Next photo
			Return dataset
		End Function

		''' <summary>
		''' Gets a list of photos of the specified user and returns the result in a dataset
		''' </summary>
		''' <param name="userId">Id of the user who's photo to search</param>
		''' <returns>Dataset containing list of photos of the specified user</returns>
		''' <exception cref="InvalidOperationException">The Application Key is not set</exception>
		''' <exception cref="ArgumentException">The userId is not specified</exception>
		Public Function SearchByUserId_DS(ByVal userId As String) As DS_Flickr
			Dim photos As List(Of PhotoData) = SearchByUserId(userId)
			Dim dataset As DS_Flickr = New DS_Flickr()
			For Each photo As PhotoData In photos
				Dim photoDetailsRow As DS_Flickr.PhotoDetailsRow = dataset.PhotoDetails.AddPhotoDetailsRow(photo.PhotoDetails.Owner, photo.PhotoDetails.Secret, photo.PhotoDetails.Server, photo.PhotoDetails.Farm, photo.PhotoDetails.IsPublic, photo.PhotoDetails.IsFriend, photo.PhotoDetails.IsFamily)

				dataset.PhotoData.AddPhotoDataRow(photo.Id, photo.Title, photo.Url, photoDetailsRow)
			Next photo
			Return dataset
		End Function

		''' <summary>
		''' Searches for a list of photos matching the criteria and returns the result in a dataset
		''' </summary>
		''' <param name="text">A free text search</param>
		''' <returns>Dataset containing list of photos matching the criteria</returns>
		''' <exception cref="InvalidOperationException">The Application Key is not set</exception>
		''' <exception cref="ArgumentException">The text is not specified</exception>
		Public Function Search_DS(ByVal text As String) As DS_Flickr
			Dim photos As List(Of PhotoData) = Search(text)
			Dim dataset As DS_Flickr = New DS_Flickr()
			For Each photo As PhotoData In photos
				Dim photoDetailsRow As DS_Flickr.PhotoDetailsRow = dataset.PhotoDetails.AddPhotoDetailsRow(photo.PhotoDetails.Owner, photo.PhotoDetails.Secret, photo.PhotoDetails.Server, photo.PhotoDetails.Farm, photo.PhotoDetails.IsPublic, photo.PhotoDetails.IsFriend, photo.PhotoDetails.IsFamily)

				dataset.PhotoData.AddPhotoDataRow(photo.Id, photo.Title, photo.Url, photoDetailsRow)
			Next photo
			Return dataset
		End Function
		#End Region

		#Region "Private functions"
		''' <summary>
		''' Prepare the query string based on parameters
		''' </summary>
		''' <param name="userId">Id of the user who's photo to search</param>
		''' <param name="tags">List of tags</param>
		''' <param name="tagMode">tag mode (Either 'any' for an OR combination of tags or 'all' for an AND combination)</param>
		''' <param name="text">A free text search</param>
		''' <param name="minUploadDate">Minimum upload date</param>
		''' <param name="maxUploadDate">Maximum upload date</param>
		''' <param name="minTakenDate">Minimum taken date</param>
		''' <param name="maxTakenDate">Maximum taken date</param>
		''' <param name="license">The license id for photos</param>
		''' <param name="sort">The order in which to sort returned photos</param>
		''' <param name="privacyFilter">Return photos only matching a certain privacy level</param>
		''' <param name="bbox">A list of 4 values defining the Bounding Box of the area that will be searched</param>
		''' <param name="accuracy">Recorded accuracy level of the location information</param>
		''' <param name="extras">A list of extra information to fetch for each returned record</param>
		''' <param name="numberOfPagesForResult">The page of results to return</param>
		''' <param name="resultsPerPage">Results per page</param>
		''' <returns>Request Url</returns>
		Private Function GetQueryString(ByVal userId As String, ByVal tags As String, ByVal tagMode As String, ByVal text As String, ByVal minUploadDate As String, ByVal maxUploadDate As String, ByVal minTakenDate As String, ByVal maxTakenDate As String, ByVal license As String, ByVal sort As String, ByVal privacyFilter As Nullable(Of Integer), ByVal bbox As String, ByVal accuracy As Nullable(Of Integer), ByVal extras As String, ByVal numberOfPagesForResult As Nullable(Of Integer), ByVal resultsPerPage As Nullable(Of Integer)) As String
			Dim validParameterFound As Boolean = False
			Dim queryBuilder As StringBuilder = New StringBuilder()

			' Add application key
			queryBuilder.Append("&api_key=")
			queryBuilder.Append(Me.ApplicationKey)

			' Add userId
			If (Not String.IsNullOrEmpty(userId)) AndAlso userId.Trim().Length > 0 Then
				queryBuilder.Append("&user_id=")
				queryBuilder.Append(userId)
				validParameterFound = True
			End If

			' Add tags to search
			If (Not String.IsNullOrEmpty(tags)) AndAlso tags.Trim().Length > 0 Then
				queryBuilder.Append("&tags=")
				queryBuilder.Append(tags)
				validParameterFound = True
			End If

			' Add tag mode
			If (Not String.IsNullOrEmpty(tagMode)) AndAlso tagMode.Trim().Length > 0 Then
				queryBuilder.Append("&tag_mode=")
				queryBuilder.Append(tagMode)
				validParameterFound = True
			End If

			' Add text 
			If (Not String.IsNullOrEmpty(text)) AndAlso text.Trim().Length > 0 Then
				queryBuilder.Append("&text=")
				queryBuilder.Append(text)
				validParameterFound = True
			End If

			' Add min upload date
			If (Not String.IsNullOrEmpty(minUploadDate)) AndAlso minUploadDate.Trim().Length > 0 Then
				queryBuilder.Append("&min_upload_date=")
				queryBuilder.Append(minUploadDate)
				validParameterFound = True
			End If

			' Add max upload date
			If (Not String.IsNullOrEmpty(maxUploadDate)) AndAlso maxUploadDate.Trim().Length > 0 Then
				queryBuilder.Append("&max_upload_date=")
				queryBuilder.Append(maxUploadDate)
				validParameterFound = True
			End If

			' Add min taken date
			If (Not String.IsNullOrEmpty(minTakenDate)) AndAlso minTakenDate.Trim().Length > 0 Then
				queryBuilder.Append("&min_taken_date=")
				queryBuilder.Append(minTakenDate)
				validParameterFound = True
			End If

			' Add max taken date
			If (Not String.IsNullOrEmpty(maxTakenDate)) AndAlso maxTakenDate.Trim().Length > 0 Then
				queryBuilder.Append("&max_taken_date=")
				queryBuilder.Append(maxTakenDate)
				validParameterFound = True
			End If

			' Add license 
			If (Not String.IsNullOrEmpty(license)) AndAlso license.Trim().Length > 0 Then
				queryBuilder.Append("&license=")
				queryBuilder.Append(license)
				validParameterFound = True
			End If

			' Add sort  
			If (Not String.IsNullOrEmpty(sort)) AndAlso sort.Trim().Length > 0 Then
				queryBuilder.Append("&sort=")
				queryBuilder.Append(sort)
				validParameterFound = True
			End If

			' Add privacy filter   
			If privacyFilter.HasValue Then
				queryBuilder.Append("&privacy_filter=")
				queryBuilder.Append(privacyFilter.Value.ToString(CultureInfo.InvariantCulture))
				validParameterFound = True
			End If

			' Add bbox 
			If (Not String.IsNullOrEmpty(bbox)) AndAlso bbox.Trim().Length > 0 Then
				queryBuilder.Append("&bbox=")
				queryBuilder.Append(bbox)
				validParameterFound = True
			End If

			' Add accuracy 
			If accuracy.HasValue Then
				queryBuilder.Append("&accuracy=")
				queryBuilder.Append(accuracy.Value.ToString(CultureInfo.InvariantCulture))
				validParameterFound = True
			End If

			' Add extras 
			If (Not String.IsNullOrEmpty(extras)) AndAlso extras.Trim().Length > 0 Then
				queryBuilder.Append("&extras=")
				queryBuilder.Append(extras)
				validParameterFound = True
			End If

			' Add numberOfPagesForResult
			If numberOfPagesForResult.HasValue Then
				queryBuilder.Append("&page=")
				queryBuilder.Append(numberOfPagesForResult.Value)
				validParameterFound = True
			End If

			' Add resultsPerPage
			If resultsPerPage.HasValue Then
				queryBuilder.Append("&per_page=")
				queryBuilder.Append(resultsPerPage)
				validParameterFound = True
			End If

			Dim methodName As String = Nothing

			If validParameterFound Then
				methodName = "?method=flickr.photos.search"
			Else
				methodName = "?method=flickr.photos.getrecent"
			End If

			Return Me.baseUrl & methodName & queryBuilder.ToString()
		End Function

		''' <summary>
		''' Parses the response data and extracts the photo items
		''' </summary>
		''' <param name="responseData">Response data</param>
		''' <returns>Photo item list</returns>
		Private Shared Function ParseResponseXml(ByVal responseData As String) As List(Of PhotoData)
			Dim xmlDocument As XmlDocument = New XmlDocument()
			Dim photoDataList As List(Of PhotoData) = Nothing

			xmlDocument.LoadXml(responseData)
			' Retrieve result nodes
			Dim photoNodes As XmlNodeList = xmlDocument.DocumentElement.SelectNodes("photos/photo")

			If photoNodes Is Nothing OrElse photoNodes.Count = 0 Then
				Return photoDataList
			End If

			photoDataList = New List(Of PhotoData)(photoNodes.Count)

			For i As Integer = 0 To photoNodes.Count - 1
				Dim photoNode As XmlNode = photoNodes(i)
				Dim photoData As PhotoData = New PhotoData()
				photoData.PhotoDetails = New PhotoDetails()
				photoDataList.Add(photoData)

				' Populate PhotoData object
				If Not photoNode.Attributes Is Nothing AndAlso photoNode.Attributes.Count > 0 Then
					If photoNode.Attributes("id") Is Nothing Then
						photoData.Id = Nothing
					Else
						photoData.Id = photoNode.Attributes("id").Value
					End If

					If photoNode.Attributes("owner") Is Nothing Then
						photoData.PhotoDetails.Owner = Nothing
					Else
						photoData.PhotoDetails.Owner = photoNode.Attributes("owner").Value
					End If

					If photoNode.Attributes("secret") Is Nothing Then
						photoData.PhotoDetails.Secret = Nothing
					Else
						photoData.PhotoDetails.Secret = photoNode.Attributes("secret").Value
					End If

					If photoNode.Attributes("server") Is Nothing Then
						photoData.PhotoDetails.Server = 0
					Else
						photoData.PhotoDetails.Server = Int32.Parse(photoNode.Attributes("server").Value, CultureInfo.InvariantCulture)
					End If

					If photoNode.Attributes("farm") Is Nothing Then
						photoData.PhotoDetails.Farm = 0
					Else
						photoData.PhotoDetails.Farm = Int32.Parse(photoNode.Attributes("farm").Value, CultureInfo.InvariantCulture)
					End If

					If photoNode.Attributes("title") Is Nothing Then
						photoData.Title = Nothing
					Else
						photoData.Title = photoNode.Attributes("title").Value
					End If

					If photoNode.Attributes("ispublic") Is Nothing Then
						photoData.PhotoDetails.IsPublic = 0
					Else
						photoData.PhotoDetails.IsPublic = Int32.Parse(photoNode.Attributes("ispublic").Value, CultureInfo.InvariantCulture)
					End If

					If photoNode.Attributes("isfriend") Is Nothing Then
						photoData.PhotoDetails.IsFriend = 0
					Else
						photoData.PhotoDetails.IsFriend = Int32.Parse(photoNode.Attributes("isfriend").Value, CultureInfo.InvariantCulture)
					End If

					If photoNode.Attributes("isfamily") Is Nothing Then
						photoData.PhotoDetails.IsFamily = 0
					Else
						photoData.PhotoDetails.IsFamily = Int32.Parse(photoNode.Attributes("isfamily").Value, CultureInfo.InvariantCulture)
					End If
				End If
			Next i
			Return photoDataList
		End Function
		''' <summary>
		''' Fill the photo Url field in the photo item object
		''' </summary>
		''' <param name="photoDataList">Photo item list</param>
		Private Sub FillPhotoUrl(ByVal photoDataList As List(Of PhotoData))
			If photoDataList Is Nothing OrElse photoDataList.Count <= 0 Then
				Return
			End If

			For i As Integer = 0 To photoDataList.Count - 1
				photoDataList(i).Url = GetPhotoUrl(photoDataList(i).Id)
			Next i
		End Sub
		''' <summary>
		''' Gets the photo Url of the photo item
		''' </summary>
		''' <param name="photoId">Photo Id of the photo item whose Url is requested </param>
		''' <returns>Photo Url</returns>
		Private Function GetPhotoUrl(ByVal photoId As String) As String
			If String.IsNullOrEmpty(photoId) Then
				Return Nothing
			End If

			Dim requestUrl As String = Me.baseUrl & "?method=flickr.photos.getInfo" & "&api_key=" & Me.ApplicationKey & "&photo_id=" & photoId

			Dim responseData As String = MyBase.GetQueryResponse(requestUrl)

			Return GetUrlFromXml(responseData)
		End Function
		''' <summary>
		''' Parses the response data and extracts the photo Url
		''' </summary>
		''' <param name="responseData">Response data</param>
		''' <returns>Photo Url</returns>
		Private Shared Function GetUrlFromXml(ByVal responseData As String) As String
			' parse xml to get url of photo.
			Dim xmlDocument As XmlDocument = New XmlDocument()
			xmlDocument.LoadXml(responseData)

			' Retrieve result nodes
			Dim urlsNode As XmlNode = xmlDocument.DocumentElement.SelectSingleNode("photo/urls")

			If urlsNode Is Nothing Then
				Return Nothing
			End If

			Dim urlNodes As XmlNodeList = urlsNode.SelectNodes("url")
			If urlNodes Is Nothing OrElse urlNodes.Count <= 0 Then
				Return Nothing
			End If

			For i As Integer = 0 To urlNodes.Count - 1
				If urlNodes(i).Attributes Is Nothing OrElse urlNodes(i).Attributes.Count <= 0 OrElse urlNodes(i).Attributes("type") Is Nothing Then
					Continue For
				End If

				If String.Compare(urlNodes(i).Attributes("type").Value, "photopage", False, CultureInfo.InvariantCulture) <> 0 Then
					Continue For
				End If

				Return urlNodes(i).InnerText
			Next i
			Return Nothing
		End Function
		#End Region
	End Class

	''' <summary>
	''' Represents a photo item Flickr.com
	''' </summary>
	Public Class PhotoData
		#Region "Private Members"
		''' <summary>
		''' Id of the photo
		''' </summary>
		Private id_Renamed As String
		''' <summary>
		''' Title of the photo
		''' </summary>
		Private title_Renamed As String
		''' <summary>
		''' URL to the photo
		''' </summary>
		Private url_Renamed As String
		''' <summary>
		''' Photo details
		''' </summary>
		Private photoDetails_Renamed As PhotoDetails
		#End Region

		#Region "Public Properties"
		''' <summary>
		''' Id of the photo
		''' </summary>
		Public Property Id() As String
			Get
				Return Me.id_Renamed
			End Get
			Set(ByVal value As String)
				Me.id_Renamed = value
			End Set
		End Property
		''' <summary>
		''' Title of the photo
		''' </summary>
		Public Property Title() As String
			Get
				Return Me.title_Renamed
			End Get
			Set(ByVal value As String)
				Me.title_Renamed = value
			End Set
		End Property
		''' <summary>
		''' URL to the photo
		''' </summary>
		Public Property Url() As String
			Get
				Return Me.url_Renamed
			End Get
			Set(ByVal value As String)
				Me.url_Renamed = value
			End Set
		End Property
		''' <summary>
		''' Photo details
		''' </summary>
		Public Property PhotoDetails() As PhotoDetails
			Get
				Return Me.photoDetails_Renamed
			End Get
			Set(ByVal value As PhotoDetails)
				Me.photoDetails_Renamed = value
			End Set
		End Property
		#End Region
	End Class

	''' <summary>
	''' This class represents the photo details
	''' </summary>
	Public Class PhotoDetails
		#Region "Private Members"
		''' <summary>
		''' Owner of the photo
		''' </summary>
		Private owner_Renamed As String
		''' <summary>
		''' Secret word
		''' </summary>
		Private secret_Renamed As String
		''' <summary>
		''' Server id
		''' </summary>
		Private server_Renamed As Integer
		''' <summary>
		''' Photo farm
		''' </summary>
		Private farm_Renamed As Integer
		''' <summary>
		''' 1 if photo is public
		''' </summary>
		Private isPublic_Renamed As Integer
		''' <summary>
		''' 1 if is a friend
		''' </summary>
		Private isFriend_Renamed As Integer
		''' <summary>
		''' 1 if is of family
		''' </summary>
		Private isFamily_Renamed As Integer
		#End Region

		#Region "Public Properties"
		''' <summary>
		''' Owner of the photo
		''' </summary>
		Public Property Owner() As String
			Get
				Return Me.owner_Renamed
			End Get
			Set(ByVal value As String)
				Me.owner_Renamed = value
			End Set
		End Property
		''' <summary>
		''' Secret word
		''' </summary>
		Public Property Secret() As String
			Get
				Return Me.secret_Renamed
			End Get
			Set(ByVal value As String)
				Me.secret_Renamed = value
			End Set
		End Property
		''' <summary>
		''' Server id
		''' </summary>
		Public Property Server() As Integer
			Get
				Return Me.server_Renamed
			End Get
			Set(ByVal value As Integer)
				Me.server_Renamed = value
			End Set
		End Property
		''' <summary>
		''' Photo farm
		''' </summary>
		Public Property Farm() As Integer
			Get
				Return Me.farm_Renamed
			End Get
			Set(ByVal value As Integer)
				Me.farm_Renamed = value
			End Set
		End Property
		''' <summary>
		''' 1 if photo is public
		''' </summary>
		Public Property IsPublic() As Integer
			Get
				Return Me.isPublic_Renamed
			End Get
			Set(ByVal value As Integer)
				Me.isPublic_Renamed = value
			End Set
		End Property
		''' <summary>
		''' 1 if is a friend
		''' </summary>
		Public Property IsFriend() As Integer
			Get
				Return Me.isFriend_Renamed
			End Get
			Set(ByVal value As Integer)
				Me.isFriend_Renamed = value
			End Set
		End Property
		''' <summary>
		''' 1 if is of family
		''' </summary>
		Public Property IsFamily() As Integer
			Get
				Return Me.isFamily_Renamed
			End Get
			Set(ByVal value As Integer)
				Me.isFamily_Renamed = value
			End Set
		End Property
		#End Region
	End Class
End Namespace